﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using DevExpress.Web.ASPxEditors;

namespace ArrowWebParts
{
    class SPFileSelectorInvalidFolderException : Exception
    {
        public SPFileSelectorInvalidFolderException()
            : base()
        {

        }

        public SPFileSelectorInvalidFolderException(string message)
            : base(message)
        {

        }
    }

    public class SPFileSelectorFolderInfo : Control
    {
        #region Controls
        /// <summary>
        /// Hidden field that is populated with the folder's absolute url.
        /// </summary>
        private HiddenField hidUrl;

        #endregion Controls

        #region Private Property Variables
        /// <summary>
        /// the folder's name (eg file.ext)
        /// </summary>
        private string _folderName;
        /// <summary>
        /// guid of the folder's folder
        /// </summary>
        private string _folderGuid;
        /// <summary>
        /// url of the folder's doc lib
        /// </summary>
        private string _libUrl;
        /// <summary>
        /// guid of the folder's doc lib
        /// </summary>
        private string _libGuid;
        /// <summary>
        /// guid of the folder's web
        /// </summary>
        private string _webGuid;
        /// <summary>
        /// guid of the folder's site collection
        /// </summary>
        private string _siteGuid;
        /// <summary>
        /// Indicates if this is a valid SPFolder. False when the URL refers to an SPWeb.
        /// </summary>
        private bool _isSPFolder = false;

        #endregion Private Property Variables

        #region Properties
        /// <summary>
        /// The folder's name
        /// </summary>
        public string Name
        {
            get
            {
                return _folderName;
            }
        }
        /// <summary>
        /// The folder's guid
        /// </summary>
        public string Guid
        {
            get
            {
                return _folderGuid;
            }
        }
        /// <summary>
        /// the folder's absolute url
        /// </summary>
        public string Url
        {
            get
            {
                return hidUrl.Value;
            }
        }
        /// <summary>
        /// url of the folder's doc lib
        /// </summary>
        public string LibUrl
        {
            get
            {
                return _libUrl;
            }
        }
        /// <summary>
        /// guid of the folder's doc lib
        /// </summary>
        public string LibGuid
        {
            get
            {
                return _libGuid;
            }
        }
        /// <summary>
        /// guid of the folder's web
        /// </summary>
        public string WebGuid
        {
            get
            {
                return _webGuid;
            }
        }
        /// <summary>
        /// guid of the folder's site collection
        /// </summary>
        public string SiteGuid
        {
            get
            {
                return _siteGuid;
            }
        }
        /// <summary>
        /// <para>For use with the SPFileSelector control.</para>
        /// <para>client ID of the HiddenField that is populated with the folder's url</para>
        /// </summary>
        public string HidUrlClientID
        {
            get
            {
                if (hidUrl == null)
                    return null;
                return hidUrl.ClientID;
            }
        }
        /// <summary>
        /// Indicates if the info describes an SPFolder. False when the info describes an SPWeb.
        /// </summary>
        public bool IsSPFolder
        {
            get
            {
                return _isSPFolder;
            }
        }
        #endregion Properties

        #region Constructors
        /// <summary>
        /// Creates a new SPFileSelectorInfo control
        /// </summary>
        public SPFileSelectorFolderInfo()
        {
            hidUrl = new HiddenField();
            hidUrl.ValueChanged += new EventHandler(hidUrl_ValueChanged);
            Controls.Add(hidUrl);
        }
        #endregion Constructors

        #region Event Handlers
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            // load the folder info; may change later if hidUrl.ValueChanged is fired
            loadFolderInfoOnPostBack();
        }

        void hidUrl_ValueChanged(object sender, EventArgs e)
        {
            // reload the folder info when the url changes here on the server
            loadFolderInfoOnPostBack();
        }
        #endregion Event Handlers

        #region Helper Methods
        /// <summary>
        /// loads the folder info from the selected folder url
        /// </summary>
        private void loadFolderInfo()
        {
            // open the site collection
            using (SPSite site = new SPSite(Url))
            {
                _siteGuid = site.ID.ToString();

                // open the web
                using (SPWeb web = site.OpenWeb())
                {
                    _webGuid = web.ID.ToString();

                    // get the file's folder
                    SPFolder folder = web.GetFolder(Url);
                    _folderName = folder.Name;
                    if (folder.ParentListId.Equals(System.Guid.Empty))
                    {
                        _folderName = web.Title;
                        throw new SPFileSelectorInvalidFolderException("The selected item is not a folder, it is a website.");
                    }
                    _folderGuid = folder.UniqueId.ToString();

                    // get the file's document library
                    _libGuid = folder.ParentListId.ToString();
                    SPDocumentLibrary lib = ArrowUtils.getDocLibObject(WebGuid, LibGuid);
                    _libUrl = string.Format("{0}/{1}", web.Url, lib.RootFolder.Url);
                }
            }
        }
        /// <summary>
        /// If the Url is not null or empty, and the page is on a postback, then this
        /// will call loadFolderInfo and set _isSPFolder appropriately.
        /// </summary>
        private void loadFolderInfoOnPostBack()
        {
            // load folder info on postbacks
            if (Page.IsPostBack && !string.IsNullOrEmpty(Url))
            {
                try
                {
                    loadFolderInfo();
                    _isSPFolder = true;
                }
                catch (SPFileSelectorInvalidFolderException ex)
                {
                    _isSPFolder = false;
                }
            }
        }
        #endregion Helper Methods
    }

    /// <summary>
    /// Contains file information for a file selected from SPFileSelector.
    /// </summary>
    class SPFileSelectorFileInfo : Control
    {
        #region Controls
        /// <summary>
        /// Hidden field that is populated with the file's absolute url.
        /// </summary>
        private HiddenField hidUrl;

        #endregion Controls

        #region Private Property Variables
        /// <summary>
        /// the file's name (eg file.ext)
        /// </summary>
        private string _fileName;
        /// <summary>
        /// the file's guid
        /// </summary>
        private string _fileGuid;
        /// <summary>
        /// guid of the file's folder
        /// </summary>
        private string _folderGuid;
        /// <summary>
        /// url of the file's doc lib
        /// </summary>
        private string _libUrl;
        /// <summary>
        /// guid of the file's doc lib
        /// </summary>
        private string _libGuid;
        /// <summary>
        /// guid of the file's web
        /// </summary>
        private string _webGuid;
        /// <summary>
        /// guid of the file's site collection
        /// </summary>
        private string _siteGuid;
        /// <summary>
        /// indicates if access is denied to the specified file
        /// </summary>
        private bool _isAccessDenied = false;

        #endregion Private Property Variables

        #region Properties
        /// <summary>
        /// The file's name
        /// </summary>
        public string Name
        {
            get
            {
                return _fileName;
            }
        }
        /// <summary>
        /// The file's guid
        /// </summary>
        public string Guid
        {
            get
            {
                return _fileGuid;
            }
        }
        /// <summary>
        /// the file's absolute url
        /// </summary>
        public string Url
        {
            get
            {
                return hidUrl.Value;
            }
        }
        /// <summary>
        /// guid of the file's folder
        /// </summary>
        public string FolderGuid
        {
            get
            {
                return _folderGuid;
            }
        }
        /// <summary>
        /// url of the file's doc lib
        /// </summary>
        public string LibUrl
        {
            get
            {
                return _libUrl;
            }
        }
        /// <summary>
        /// guid of the file's doc lib
        /// </summary>
        public string LibGuid
        {
            get
            {
                return _libGuid;
            }
        }
        /// <summary>
        /// guid of the file's web
        /// </summary>
        public string WebGuid
        {
            get
            {
                return _webGuid;
            }
        }
        /// <summary>
        /// guid of the file's site collection
        /// </summary>
        public string SiteGuid
        {
            get
            {
                return _siteGuid;
            }
        }
        /// <summary>
        /// <para>For use with the SPFileSelector control.</para>
        /// <para>client ID of the HiddenField that is populated with the file's url</para>
        /// </summary>
        public string HidUrlClientID
        {
            get
            {
                if (hidUrl == null)
                    return null;
                return hidUrl.ClientID;
            }
        }
        /// <summary>
        /// Indicates if an UnauthorizedAccessException occured upon attempting to retrieve the file.
        /// </summary>
        public bool IsAccessDenied
        {
            get
            {
                return _isAccessDenied;
            }
        }
        #endregion Properties

        #region Constructors
        /// <summary>
        /// Creates a new SPFileSelectorInfo control
        /// </summary>
        public SPFileSelectorFileInfo()
        {
            hidUrl = new HiddenField();
            Controls.Add(hidUrl);
        }
        #endregion Constructors

        #region Event Handlers
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (Page.IsPostBack && !string.IsNullOrEmpty(Url))
            {
                _isAccessDenied = false;
                bool originalCatchValue = SPSecurity.CatchAccessDeniedException;
                SPSecurity.CatchAccessDeniedException = false;
                try
                {
                    loadFileInfo();
                }
                catch (UnauthorizedAccessException ex)
                {
                    _isAccessDenied = true;
                }
                finally
                {
                    SPSecurity.CatchAccessDeniedException = originalCatchValue;
                }
            }
        }
        #endregion Event Handlers

        #region Helper Methods
        /// <summary>
        /// loads the file info from the selected file url
        /// </summary>
        private void loadFileInfo()
        {
            // open the site collection
            using (SPSite site = new SPSite(Url))
            {
                _siteGuid = site.ID.ToString();

                // open the web
                using (SPWeb web = site.OpenWeb())
                {
                    _webGuid = web.ID.ToString();

                    // get the file
                    SPFile file = web.GetFile(Url);
                    _fileName = ArrowUtils.SubstringAfterLastSlash(Url);
                    _fileGuid = file.UniqueId.ToString();

                    // get the file's folder
                    SPFolder folder = file.ParentFolder;
                    _folderGuid = folder.UniqueId.ToString();

                    // get the file's document library
                    _libGuid = folder.ParentListId.ToString();
                    SPDocumentLibrary lib = ArrowUtils.getDocLibObject(WebGuid, LibGuid);
                    _libUrl = string.Format("{0}/{1}", web.Url, lib.RootFolder.Url);
                }
            }
        }
        #endregion Helper Methods
    }

    /// <summary>
    /// A control for selecting a file from an SPSiteCollection.
    /// </summary>
    class SPFileSelector : Control, INamingContainer
    {
        public enum Mode
        {
            File,
            Folder
        }

        private SPFileSelectorFileInfo _selectedFile;
        private SPFileSelectorFolderInfo _selectedFolder;
        private Label lblSelectedItem;
        private ASPxButton btnBrowse;
        private Mode mode = Mode.File;

        public SPFileSelectorFileInfo SelectedFile
        {
            get
            {
                return _selectedFile;
            }
        }

        public SPFileSelectorFolderInfo SelectedFolder
        {
            get
            {
                return _selectedFolder;
            }
        }

        public SPFileSelector(Mode selectionMode)
            : base()
        {
            // layout table
            Table tblLayout = new Table();
            Controls.Add(tblLayout);
            TableRow row = new TableRow();
            tblLayout.Rows.Add(row);
            TableCell cell0 = new TableCell();
            row.Cells.Add(cell0);
            TableCell cell1 = new TableCell();
            row.Cells.Add(cell1);

            // selected file label
            lblSelectedItem = new Label();
            lblSelectedItem.Text = "No file selected...";
            cell0.Controls.Add(lblSelectedItem);

            // browse button
            btnBrowse = new ASPxButton();
            btnBrowse.AutoPostBack = false;
            btnBrowse.Text = "Browse...";
            cell1.Controls.Add(btnBrowse);

            // set the mode
            mode = selectionMode;

            if (mode == Mode.File)
            {
                // file info control
                _selectedFile = new SPFileSelectorFileInfo();
                Controls.Add(_selectedFile);
            }
            else // mode == Mode.Folder
            {
                // folder info control
                _selectedFolder = new SPFileSelectorFolderInfo();
                Controls.Add(_selectedFolder);
            }
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            // javascript block; set here because we need client IDs
            // we need a new js block for each file selector on the page so that the browser window
            // references the correct one
            for (int i = 0; ; i++)
            {
                if (!Page.ClientScript.IsClientScriptBlockRegistered(GetType(), "ArrowFileSelectorScript_" + i))
                {
                    if (mode == Mode.File)
                    {
                        Page.ClientScript.RegisterClientScriptBlock(
                            GetType(),
                            "ArrowFileSelectorScript_" + i,
                            CreateScriptBlock(this, lblSelectedItem.ClientID, _selectedFile.HidUrlClientID),
                            true);
                    }
                    else // mode == Mode.Folder
                    {
                        Page.ClientScript.RegisterClientScriptBlock(
                            GetType(),
                            "ArrowFileSelectorScript_" + i,
                            CreateScriptBlock(this, lblSelectedItem.ClientID, _selectedFolder.HidUrlClientID),
                            true);
                    }
                    break;
                }
            }

            // button click event handler; set here because we need client IDs
            if (mode == Mode.File)
            {
                btnBrowse.ClientSideEvents.Click = @"function(s,e) {window.open('" +
                    SPContext.Current.Web.Url +
                    "/_layouts/ArrowWebParts/Browser.aspx?type=file&ctrl=" + this.ClientID +
                    @"','_blank','height=530,width=715,location=no,resizable=yes');}";
            }
            else // mode == Mode.Folder
            {
                btnBrowse.ClientSideEvents.Click = @"function(s,e) {window.open('" +
                    SPContext.Current.Web.Url +
                    "/_layouts/ArrowWebParts/Browser.aspx?type=folder&ctrl=" + this.ClientID +
                    @"','_blank','height=530,width=715,location=no,resizable=yes');}";
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            // restore the label value
            if (Page.IsPostBack)
            {
                if (mode == Mode.File && !string.IsNullOrEmpty(_selectedFile.Name))
                    lblSelectedItem.Text = _selectedFile.Name;
                else if (mode == Mode.Folder && !string.IsNullOrEmpty(_selectedFolder.Name))
                    lblSelectedItem.Text = _selectedFolder.Name;
            }
        }

        /// <summary>
        /// <para>Creates the javascript block using the specified Label and HiddenField.</para>
        /// <para>Creates the browser_FileItemClick(string, string) javascript method.</para>
        /// <para>The browser_FileItemClick(string, string) method sets the display Label to display the selected file info.</para>
        /// <para>It also stores the file's absolute URL inside HiddenField.</para>
        /// </summary>
        /// <param name="lblDisplayCID">client ID of the display Label</param>
        /// <param name="hidCID">client ID of the HiddenField</param>
        /// <returns></returns>
        private static string CreateScriptBlock(SPFileSelector spfs, string lblDisplayCID, string hidCID)
        {
            return
            @"
            function " + spfs.ClientID + @"browser_FileItemClick(fileName, fileUrl)
            {
                //debugger;
                var label = document.getElementById(""" + lblDisplayCID + @""");
                label.innerText = fileName;
                
                var hidName = document.getElementById(""" + hidCID + @""");
                hidName.value = fileUrl;
            }
            ";

            /*
             * */
        }
    }
}
